package com.monkey.luffer.springboot.demo.basis.springAop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: dengkaibing
 * @Description:
 * @Date: 2021/2/25 11:03
 * @company 甜橙金融
 * @Version: 1.0
 */
@Aspect
@Slf4j
@Component
public class OrderAop {
    @Autowired
    private ApplicationContext applicationContext;
    /**
     * execution+(* 必须有空格+全限定方法名）
     */
    @Pointcut("@annotation(com.monkey.luffer.springboot.demo.basis.springAop.annotion.Adapter)")
    public void commonMethod() {
    }
    private static String[] types = {"java.lang.Integer", "java.lang.Double",
        "java.lang.Float", "java.lang.Long", "java.lang.Short",
        "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
        "java.lang.String", "int", "double", "long", "short", "byte",
        "boolean", "char", "float"};

    /**
     * 环绕通知
     * 1:可以直接在目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作
     * 2:可以决定目标方法在什么时候执行,如何执行甚至完全可以阻止目标方法的执行
     * 3:可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值;当需要改变目标方法的返回值是,只能使用Around
     * 4:切点有返回值 则环绕通知必须有返回值，否则只返回null
     * 5:环绕通知类似于动态代理的全过程：方法参数必须是ProceedingJoinPoint 否则目标方法不执行
     */
    @Around("commonMethod()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        log.info("前置通知！");
        try {
            Object student = applicationContext.getBean("student");
            log.info("获取bean:{}",student);
            //仅环绕通知@Around支持当前方法参数可
            //切面中不调用当前方法 切点是不执行。
//            Object proceed = point.proceed();
            // 参数值
            Object[] args = joinPoint.getArgs();
            ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
            //获取目标方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            System.out.println(parameterAnnotations);
            //获取目标方法请求参数
            String[] parameterNames = pnd.getParameterNames(method);
            Map<String, Object> paramMap = new HashMap<>(32);
            for (int i = 0; i < parameterNames.length; i++) {
                for (String t : types){
                    paramMap.put(parameterNames[i], args[i]);
                }

            }
            log.info("参数列表:{}",paramMap);
        }catch (Exception e) {

        }
        log.info("后置通知！");

        return "success-123";

    }

    /**
     * 后置通知异常也执行
     */
    @Before(value = "commonMethod()")
    public void before(JoinPoint point){
        try {
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

    }
    /**
     * 后置通知异常不执行
     */
//    @AfterReturning("commonMethod()")  异常不执行
    public void afterReturning(){
        log.info("后置通知！");
    }
//    /**
//     * 异常才执行
//     */
//    @AfterThrowing("commonMethod()")
//    public void afterThrowing(){
//        log.info("后置通知！");
//    }
////    @After("commonMethod()")
//    public  void after(){
//        log.info("后置通知！");
//    }


}
